// @(#)proto.cc
// Time-stamp: <2009-02-02 02:05:31 root>
// Copyright 2008 Julian Qian
// Author: root@localhost.localdomain
// Version: $Id: proto.cc,v 0.0 2008/11/16 11:29:03 root Exp $
// Keywords: 

#include "proto.hh"


Proto::Proto(char *dev_buf, ip_t dev_ip = 0, bool dev_auth = false):
    _ip(dev_ip), _auth(dev_auth) {

    buffer_init(dev_buf);

    // bufptr = _buffer;
}

Proto::Proto(const Proto& pro){
    // bufptr = _buffer;
    *this = pro;
}

Proto::Proto(){
    _devid = 0;
    _alarm = 0;
    _ip = 0;
    _stat = 0;
    _up_time = 0;
    _sequence = 0;
    _auth = false;
    bufptr = NULL;
}

Proto::~Proto(){
    // delete bufptr;
}

void
Proto::buffer_init(char *buf){
    // bufptr = new char[PROTO_LENG];
    // memcpy(_buffer, buf, PROTO_LENG);
    // bufptr = _buffer;
    bufptr = buf;
    
    // devid
    _devid = (devid_t) char2int( bufptr + PROTO_DEVI, DEVID_NUM);
    _alarm = (alarm_t) bufptr[PROTO_DATA];
    _stat = get_buf_stat();
    _sequence  = char2int(bufptr + PROTO_SEQU, SEQU_NUM);
    // up_time
    up_time();
}

char*
Proto::composite(char* buf, devid_t devid, int seq, type_t type, alarm_t alarm){ 
    // combine buffer package, omitted to reserve original.
    // 1: length
    buf[0] = 0x0C;
    // 2~3: Device board address
    buf[1] = 0x00;
    buf[2] = 0x28;
    // 4~6: Device Unique ID
    int2char(devid, buf + PROTO_DEVI, DEVID_NUM);
    // 7~8: device version
    buf[6] = 0x01;
    buf[7] = 0x00;
    // 9~10: function description
    buf[8] = 0xf0;
    buf[9] = 0x00;
    // 11~12: Reserved
    buf[10] = 0x00;
    buf[11] = 0x00;
    // 13~14: sequance number
    // int2char(seq, buf + PROTO_SEQU, SEQU_NUM);
    buf[12] = 0x00;
    int2char(seq, buf + 13, 1);
    // 15: data length
    buf[14] = 0x02;
    // 16: data type
    buf[15] = type;  // 0x07: send from server 
    // 17: data detail
    buf[16] = alarm;
    return buf;
}

bool
Proto::do_reply(){
    // LDebug("Bufptr is reply:" << bufptr[PROTO_TYPE]);
    
    switch(bufptr[PROTO_TYPE]){
    case CLI_RESET:
        set_buf_stat(SER_RESET, _alarm);
        return true;
    case CLI_ALARM:
        // TODO: update status into database
        set_buf_stat(SER_ALARM, _alarm);
        return true;
    case CLI_DEFEN:
        set_buf_stat(SER_DEFEN, _alarm);
        return true;
    case CLI_DEFEN_AUTO:
        set_buf_stat(SER_DEFEN_AUTO, _alarm);
        return true;
    case CLI_TIMER:
        return false;
    }
    return false;
}

bool
Proto::do_alarm(){
    switch(_alarm){
    case CLI_ALARM:
        return true;
    default:
        return false;
    }
}

void
Proto::set_buf_stat(char type, char data){
	bufptr[PROTO_TYPE] = type;
	bufptr[PROTO_DATA] = data;
}

stat_t
Proto::get_buf_stat(){
	stat_t stat = STAT_NULLL;
	switch( bufptr[PROTO_TYPE] ){
	case CLI_RESET:
		stat = STAT_RESET;
		break;
	case CLI_ALARM:
        switch(bufptr[PROTO_DATA]){
		case ALARM_MANUA:
			stat = STAT_ALARM; break;
		case ALARM_DOORR:
			stat = STAT_DOORR; break;
		case ALARM_INFRA:
			stat = STAT_INFRA; break;
        case ALARM_HELPP:
            stat = STAT_HELPP; break;
        case ALARM_SMOKE:
            stat = STAT_SMOKE; break;
        case ALARM_GASSS:
            stat = STAT_GASSS; break;
        case ALARM_GLASS:
            stat = STAT_GLASS; break;
        case ALARM_WATER:
            stat = STAT_WATER; break;
        }
        break;
	case CLI_TIMER:
        // ATT: I think STAT_TIMER should be omitted,
        // it's different with other status
        // stat = STAT_TIMER;
	case CLI_UPDATE:
		switch(bufptr[PROTO_DATA]){
		case CTRL_DEFEN:
			stat = STAT_DEFEN; break;
		case CTRL_UNDEF:
			stat = STAT_UNDEF; break;
		}
		break;        
	case CLI_DEFEN: // receive
		switch(bufptr[PROTO_DATA]){
		case CTRL_DEFEN:
			stat = STAT_BUTON; break;
		case CTRL_UNDEF:
			stat = STAT_UNBTN; break;
		}
		break;
    case SER_UPDATE:
		switch(bufptr[PROTO_DATA]){
		case CTRL_DEFEN:
			stat = STAT_PHONE; break;
		case CTRL_UNDEF:
			stat = STAT_UNPHO; break;
		}
		break;
	default:
		stat = STAT_OTHER;
		break;
	}
	return stat;
}

int
Proto::char2int(char *buf, int len){
    int i;
    int intt = 0;
    for(i = 0; i< len; i++){
        intt = intt << 8;
        intt += (unsigned char)buf[i];
    }
    return intt;
}

void
Proto::int2char(unsigned int intt, char* buf, int len){
	int i;
    char chr;
    // ATTENTION: len is not more than 4, because int only contains 4 bytes.
	for (i=0; i<len; i++) {
		chr = intt >> 8*i;
		*(buf+len-i-1) = chr;
	}
}
